﻿namespace Tencent
{
    using System;
    using System.Collections;
    using System.Security.Cryptography;
    using System.Text;
    using System.Xml;

    public class WXBizMsgCrypt
    {
        private string m_sAppID;
        private string m_sEncodingAESKey;
        private string m_sToken;

        public WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sAppID)
        {
            this.m_sToken = sToken;
            this.m_sAppID = sAppID;
            this.m_sEncodingAESKey = sEncodingAESKey;
        }

        public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg)
        {
            string sEncryptMsg;
            if (this.m_sEncodingAESKey.Length != 0x2b)
            {
                return -40004;
            }
            XmlDocument doc = new XmlDocument();
            try
            {
                doc.LoadXml(sPostData);
                sEncryptMsg = doc.FirstChild["Encrypt"].InnerText;
            }
            catch (Exception)
            {
                return -40002;
            }
            int ret = 0;
            ret = VerifySignature(this.m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature);
            if (ret != 0)
            {
                return ret;
            }
            string cpid = "";
            try
            {
                sMsg = Cryptography.AES_decrypt(sEncryptMsg, this.m_sEncodingAESKey, ref cpid);
            }
            catch (FormatException)
            {
                return -40010;
            }
            catch (Exception)
            {
                return -40007;
            }
            if (cpid != this.m_sAppID)
            {
                return -40005;
            }
            return 0;
        }

        public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg)
        {
            if (this.m_sEncodingAESKey.Length != 0x2b)
            {
                return -40004;
            }
            string raw = "";
            try
            {
                raw = Cryptography.AES_encrypt(sReplyMsg, this.m_sEncodingAESKey, this.m_sAppID);
            }
            catch (Exception)
            {
                return -40006;
            }
            string MsgSigature = "";
            int ret = 0;
            ret = GenarateSinature(this.m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature);
            if (ret != 0)
            {
                return ret;
            }
            sEncryptMsg = "";
            string EncryptLabelHead = "<Encrypt><![CDATA[";
            string EncryptLabelTail = "]]></Encrypt>";
            string MsgSigLabelHead = "<MsgSignature><![CDATA[";
            string MsgSigLabelTail = "]]></MsgSignature>";
            string TimeStampLabelHead = "<TimeStamp><![CDATA[";
            string TimeStampLabelTail = "]]></TimeStamp>";
            string NonceLabelHead = "<Nonce><![CDATA[";
            string NonceLabelTail = "]]></Nonce>";
            sEncryptMsg = sEncryptMsg + "<xml>" + EncryptLabelHead + raw + EncryptLabelTail;
            sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail;
            sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail;
            sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail;
            sEncryptMsg = sEncryptMsg + "</xml>";
            return 0;
        }

        public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, ref string sMsgSignature)
        {
            ArrayList AL = new ArrayList();
            AL.Add(sToken);
            AL.Add(sTimeStamp);
            AL.Add(sNonce);
            AL.Add(sMsgEncrypt);
            AL.Sort(new DictionarySort());
            string raw = "";
            for (int i = 0; i < AL.Count; i++)
            {
                raw = raw + AL[i];
            }
            string hash = "";
            try
            {
                SHA1 sha = new SHA1CryptoServiceProvider();
                byte[] dataToHash = new ASCIIEncoding().GetBytes(raw);
                hash = BitConverter.ToString(sha.ComputeHash(dataToHash)).Replace("-", "").ToLower();
            }
            catch (Exception)
            {
                return -40003;
            }
            sMsgSignature = hash;
            return 0;
        }

        public static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sSigture)
        {
            return VerifySignature(sToken, sTimeStamp, sNonce, "", sSigture);
        }

        public static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture)
        {
            string hash = "";
            int ret = 0;
            ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash);
            if (ret != 0)
            {
                return ret;
            }
            if (hash == sSigture)
            {
                return 0;
            }
            return -40001;
        }

        public class DictionarySort : IComparer
        {
            public int Compare(object oLeft, object oRight)
            {
                string sLeft = oLeft as string;
                string sRight = oRight as string;
                int iLeftLength = sLeft.Length;
                int iRightLength = sRight.Length;
                for (int index = 0; (index < iLeftLength) && (index < iRightLength); index++)
                {
                    if (sLeft[index] < sRight[index])
                    {
                        return -1;
                    }
                    if (sLeft[index] > sRight[index])
                    {
                        return 1;
                    }
                }
                return (iLeftLength - iRightLength);
            }
        }

        public enum WXBizMsgCryptErrorCode
        {
            WXBizMsgCrypt_ComputeSignature_Error = -40003,
            WXBizMsgCrypt_DecodeBase64_Error = -40010,
            WXBizMsgCrypt_DecryptAES_Error = -40007,
            WXBizMsgCrypt_EncodeBase64_Error = -40009,
            WXBizMsgCrypt_EncryptAES_Error = -40006,
            WXBizMsgCrypt_IllegalAesKey = -40004,
            WXBizMsgCrypt_IllegalBuffer = -40008,
            WXBizMsgCrypt_OK = 0,
            WXBizMsgCrypt_ParseXml_Error = -40002,
            WXBizMsgCrypt_ValidateAppid_Error = -40005,
            WXBizMsgCrypt_ValidateSignature_Error = -40001
        }
    }
}

